package com.yilin.platform.base_core.tools.pab.utils;

import com.yilin.platform.base_core.commons.exception.BusinessException;
import com.yilin.platform.base_core.commons.utils.*;
import com.yilin.platform.base_core.tools.cmbc.utils.XmlBodyTools;
import com.yilin.platform.base_core.tools.pab.base.*;
import com.yilin.platform.base_core.tools.pab.entity.DataXmlHeader;
import com.yilin.platform.base_core.tools.pab.entity.req.*;
import com.yilin.platform.base_core.tools.pab.entity.res.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.*;

@Component
public class PabXmlTools {
    private static Logger logger = LoggerFactory.getLogger(PabXmlTools.class);
    //######## 常量 XML_MARK 一个空格也不能更改
    private final static String XML_MARK = "<?xml version=\"1.0\" encoding=\"%s\"?>";
    private final static int DATA_HEADER_LENGTH = 222;
    public final static String RET_SUCCESS_CODE = "000000";

    @Value("${pab-bank-enterprise.req-url}")
    private String reqUrl = "http://192.168.0.201:7072";
    @Value("${pab-bank-enterprise.xml-category}")
    private String xmlCategory = "A00101";
    @Value("${pab-bank-enterprise.merchant-code}")
//    private String merchantCode = "0090108040000QY12000";
    private String merchantCode = "00901080000001111000";//查

    @Value("${pab-bank-enterprise.encoded}")
    private String encoded = "GBK";

    public static void main(String[] args) {
        PabXmlTools pabXmlTools = new PabXmlTools();
//        //构造报文头
//        DataXmlHeader header = pabXmlTools.buildXmlHeader("0238");
//        try {
//            int i = header.toString().getBytes("GBK").length;
//            System.out.println(i);
//        } catch (UnsupportedEncodingException e) {
//            e.printStackTrace();
//        }
//        //解析报文头
//        DataXmlHeader dataXmlHeader = pabXmlTools.analysisXmlHeader(header.toString());
//        System.out.println(dataXmlHeader.toString());
//        //构造报文体
//        _ReqBodyTest s001 = new _ReqBodyTest();
//        List<_ReqBodyTest.ReqBodyChild> list = new ArrayList<>();
//        List<_ReqBodyTest.ReqBodyChild.ReqBodyChildChild> ccList1 = new ArrayList<>();
//        ccList1.add(new _ReqBodyTest.ReqBodyChild.ReqBodyChildChild("CCITEM0011", "CCITEM0012"));
//        ccList1.add(new _ReqBodyTest.ReqBodyChild.ReqBodyChildChild("CCITEM0013", "CCITEM0014"));
//        List<_ReqBodyTest.ReqBodyChild.ReqBodyChildChild> ccList2 = new ArrayList<>();
//        ccList2.add(new _ReqBodyTest.ReqBodyChild.ReqBodyChildChild("CCITEM0021", "CCITEM0022"));
//        ccList2.add(new _ReqBodyTest.ReqBodyChild.ReqBodyChildChild("CCITEM0023", "CCITEM0024"));
//        list.add(new _ReqBodyTest.ReqBodyChild("ITEM001", "ITEM002", ccList1));
//        list.add(new _ReqBodyTest.ReqBodyChild("ITEM004", "ITEM005", ccList2));
//        s001.setBodyTest("测试");
//        s001.setTestList(list);
//        s001.setReqBodyChild(new _ReqBodyTest.ReqBodyChild("CCITEM0099", "CCITEM0089", null));
//        String xml = pabXmlTools.buildXmlBody(s001);
//        System.out.println(xml);
//        //解析报文体
//        _ReqBodyTest test = pabXmlTools.analysisXmlBody(xml, _ReqBodyTest.class);
//        System.out.println(test);
//        String bodyXml2 ="<?xml version=\"1.0\" encoding=\"GBK\"?><Result></Result>";
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
        /// TODO:查询测试
        ReqBody4001 reqBody4001 = new ReqBody4001();
        reqBody4001.setAccount("15000100714429");
        ResResult<ReqBody4001, ResBody4001> result = pabXmlTools.postData(reqBody4001, ResBody4001.class);
        if (result.getResHeader().getResCode().equals(RET_SUCCESS_CODE)) {
            System.out.println("正在进行查账测试，账户余额："+result.getResBody().getBalance());
        }
        /// TODO:转账测试
//        ReqBody4004 reqBody4004 = new ReqBody4004();
//        reqBody4004.setThirdVoucher(DateTimeUtil.dateNo("TN"));//转账凭证号
//        reqBody4004.setCcyCode("RMB");
//        reqBody4004.setOutAcctNo("15000101187347");//付款人账户
//        reqBody4004.setOutAcctName("平安金融测试一二");//付款人名称

//        reqBody4004.setInAcctNo("6225380092315250");//收款人账户
//        reqBody4004.setInAcctName("钭旨");//收款人账户户名
//        reqBody4004.setTranAmount("100.00");//转出金额

//        reqBody4004.setAddrFlag("1");//同城/异地标志
//        ResResult<ReqBody4004, ResBody4004> result = pabXmlTools.postData(reqBody4004, ResBody4004.class);
//        if (result.getResHeader().getResCode().equals(RET_SUCCESS_CODE)) {
//            System.out.println("正在进行转账测试，交易结果："+result.getResBody().getStt());
//        }
        /// TODO:跨行转账测试
//        ReqBody4004 reqBody4004 = new ReqBody4004();
//        reqBody4004.setThirdVoucher(DateTimeUtil.dateNo("TN"));//转账凭证号
//        reqBody4004.setCcyCode("RMB");
//        reqBody4004.setOutAcctNo("15000101187347");//付款人账户
//        reqBody4004.setOutAcctName("平安金融测试一二");//付款人名称
//        reqBody4004.setInAcctNo("6228480128020121579");//收款人账户 中国农业银行股份有限公司
//        reqBody4004.setInAcctName("桂荣");//收款人账户户名
//        reqBody4004.setTranAmount("101.00");//转出金额
//        reqBody4004.setAddrFlag("1");//同城/异地标志
//        ResResult<ReqBody4004, ResBody4004> result = pabXmlTools.postData(reqBody4004, ResBody4004.class);
//        if (result.getResHeader().getResCode().equals(RET_SUCCESS_CODE)) {
//            System.out.println("正在进行转账测试，交易结果：" + result.getResBody().getStt());
//        }
        /// TODO:查询账户当日历史交易明细
//        ReqBody4013 reqBody4013 = new ReqBody4013();
//        reqBody4013.setAcctNo("15000100714429");
//        reqBody4013.setCcyCode("RMB");
//        reqBody4013.setBeginDate("20230702");//yyyyMMdd
//        reqBody4013.setEndDate("20230813");//yyyyMMdd
//        reqBody4013.setPageNo("1");//1
//        reqBody4013.setPageSize("30");//30
//        ResResult<ReqBody4013, ResBody4013> result = pabXmlTools.postData(reqBody4013, ResBody4013.class);
//        if (result.getResHeader().getResCode().equals(RET_SUCCESS_CODE)) {
//            List<String> stringList = ArrayUtil.fieldAdd(result.getResBody().getItemList(), obj -> obj.getTxTime() + "|" + obj.getBussSeqNo() + "|" + obj.getTranAmount());
//            System.out.println("正在进行交易明细查询测试，交易明细：" + stringList);
//        }

        /// TODO:明细报表查询接口
//        ReqBodyF00101 reqBodyF00101 = new ReqBodyF00101();
//        reqBodyF00101.setQueryDate("20230809");
//        reqBodyF00101.setAccount("15000100714429");
//        ResResult<ReqBodyF00101, ResBodyF00101> result = pabXmlTools.postData(reqBodyF00101, ResBodyF00101.class);
//        if (result.getResHeader().getResCode().equals(RET_SUCCESS_CODE)) {
//            List<String> stringList = ArrayUtil.fieldAdd(result.getResBody().getItemList(), obj -> obj.getFileName()+"|"+obj.getFilePath()+"|"+obj.getRandomPwd());
//            System.out.println("正在进行明细报表查询测试，文件列表："+stringList);
//        }

        /// TODO:公共文件件下载接口    2    30
//        ReqBodyFILE03 reqBodyFILE03 = new ReqBodyFILE03();
//        reqBodyFILE03.setTradeSn(DateTimeUtil.dateNo("SN"));
//        reqBodyFILE03.setFileName("RECPDF_KHKF_15000100714429_20230809.zip");
//        reqBodyFILE03.setRandomPwd("w3plU3lqVFUsqIrfePdRcbxNoddR34fd");
//        ResResult<ReqBodyFILE03, ResBodyFILE03> result = pabXmlTools.postData(reqBodyFILE03, ResBodyFILE03.class);
//        if (result.getResHeader().getResCode().equals(RET_SUCCESS_CODE)) {
//            System.out.println("正在进行文件下载请求：已提交，流水"+reqBodyFILE03.getTradeSn()+"，请等待下载结果通知");
//        }
        //通知

        ///SFTP下载

    }


    /**
     * 发送数据
     */
    public <Req, Res> ResResult<Req, Res> postData(Req reqBody, Class<Res> cls) {
        try {
            PabTrade trade = reqBody.getClass().getAnnotation(PabTrade.class);
            ResResult<Req, Res> resResult = new ResResult<>();
            String reqBodyXml = buildXmlBody(reqBody);
            int bodyLength = reqBodyXml.getBytes(encoded).length;
            DataXmlHeader reqHeader = buildXmlHeader(trade.value(), bodyLength);
            String resXml = xmlPost(reqHeader.toString(), reqBodyXml);
            resResult.setReqData(reqHeader, reqBody);
            String resHeaderXml = StringUtil.substringByte(resXml, 0, DATA_HEADER_LENGTH, encoded);
            DataXmlHeader resHeader = analysisXmlHeader(resHeaderXml);
            String resBodyXml = StringUtil.substringByte(resXml, DATA_HEADER_LENGTH, encoded);
            Res resBody = null;
            if (StringUtil.isNotEmpty(resBodyXml)) resBody = analysisXmlBody(resBodyXml, cls);
            resResult.setResData(resHeader, resBody);
            return resResult;
        } catch (UnsupportedEncodingException e) {
            throw new BusinessException("平安银行发送数据异常");
        }
    }

    /**
     * XML请求
     */
    private String xmlPost(String xmlHeader, String xmlBody) {
        try {
            logger.info("请求头字节长度: " + xmlHeader.getBytes(encoded).length);
            logger.info("请求体字节长度: " + xmlBody.getBytes(encoded).length);
            String reqXml = xmlHeader + xmlBody;
            return HttpService.pubPost(reqXml, reqUrl, encoded);
        } catch (Exception e) {
            throw new BusinessException("平安银行XML请求异常");
        }
    }

    /**
     * 通知处理区 头
     */
    public DataXmlHeader dealNoticeXmlHeader(String pabReqXml) {
        String resHeaderXml = StringUtil.substringByte(pabReqXml, 0, DATA_HEADER_LENGTH, encoded);
        return analysisXmlHeader(resHeaderXml);
    }

    /**
     * 通知处理区 体
     */
    public <T> T dealNoticeXmlBody(String pabReqXml, Class<T> cls) {
        String bodyXml = StringUtil.substringByte(pabReqXml, DATA_HEADER_LENGTH, encoded);
        return analysisXmlBody(bodyXml, cls);
    }

    /**
     * 获取XML字节数，使用配置的编码
     */
    public int getDataXmlLength(String resBodyXml) {
        try {
            if (resBodyXml == null) return 0;
            return resBodyXml.getBytes(encoded).length;
        } catch (UnsupportedEncodingException e) {
            return 0;
        }
    }

    /**
     * 构造报文头
     * 构造报文头*注释：如果非必输域需要用空格补齐报文
     */
    private DataXmlHeader buildXmlHeader(String tradeCode, int bodyLength) {
        DataXmlHeader xmlHeader = new DataXmlHeader();
        xmlHeader.setXmlCategory(xmlCategory);
        xmlHeader.setEncoded(encoded.equals("GBK") ? "01" : "02");
        xmlHeader.setReqProtocol("02");
        xmlHeader.setBodyLength(bodyLength);
        xmlHeader.setMerchantCode(merchantCode);
        xmlHeader.setTradeCode(tradeCode);
        xmlHeader.setOptCode("00000");
        xmlHeader.setServiceType(1);//请求
        xmlHeader.setTradeDate(new Date());
        xmlHeader.setResFlowNo(DateTimeUtil.dateNo("PB"));
        xmlHeader.setIsReqFollow("0");//0-结束包
        xmlHeader.setReqFollowCount("000"); //目前仅支持000
        xmlHeader.setSignature("0");// 企业不管
        xmlHeader.setSignFormat("1"); // 企业不管
        return xmlHeader;
    }

    /**
     * 构造返回报文头
     * 构造报文头*注释：如果非必输域需要用空格补齐报文
     */
    public DataXmlHeader buildRetHeader(DataXmlHeader dataXmlHeader, int bodyLength) {
        DataXmlHeader xmlHeader = new DataXmlHeader();
        xmlHeader.setXmlCategory(xmlCategory);
        xmlHeader.setEncoded(encoded.equals("GBK") ? "01" : "02");
        xmlHeader.setReqProtocol("02");
        xmlHeader.setBodyLength(bodyLength);
        xmlHeader.setMerchantCode(merchantCode);
        xmlHeader.setTradeCode(dataXmlHeader.getTradeCode());
        xmlHeader.setOptCode("00000");
        xmlHeader.setServiceType(2);//应答
        xmlHeader.setReqDate(dataXmlHeader.getReqDate());
        xmlHeader.setReqTime(dataXmlHeader.getReqTime());
        xmlHeader.setResFlowNo(dataXmlHeader.getResFlowNo());
        xmlHeader.setResCode("000000");
        xmlHeader.setResMsg(":交易受理成功");
        xmlHeader.setIsReqFollow("0");//0-结束包
        xmlHeader.setReqFollowCount("000"); //目前仅支持000
        xmlHeader.setSignature("0");// 企业不管
        xmlHeader.setSignFormat("1"); // 企业不管
        return xmlHeader;
    }
//    A00101|01|01|00901080000001111000|0000000108|4001  |123450|1|20210915|192127|YQTEST20210915192127|                                                                                                          |000001            00000000000
//    A00101|01|01|00901080000001111000|0000000708|4001  |123450|2|20210915|192127|YQTEST20210915192127|000000:交易受理成功                                                                                       |000001            00000000000

    /**
     * 解析报文头
     * 解析报文头
     */
    public DataXmlHeader analysisXmlHeader(String headerXml) {
        DataXmlHeader xmlHeader = new DataXmlHeader();
        List<Field> fieldList = FieldUtil.getFieldsByAnnotation(xmlHeader.getClass(), SplitRange.class);
        for (Field field : fieldList) {
            SplitRange annotation = field.getAnnotation(SplitRange.class);
            String substring = StringUtil.substringByte(headerXml, annotation.begin(), annotation.len(), encoded);
            if (field.getType().equals(Integer.class)) FieldUtil.objSet(xmlHeader, field, Integer.parseInt(substring));
            else FieldUtil.objSet(xmlHeader, field, substring);
        }
        return xmlHeader;
    }

    /**
     * 构造报文体
     * 构造报文体 支持报文集合, 支持无限层
     */
    public <T> String buildXmlBody(T reqBody) {
        String versionXml = String.format(XML_MARK, encoded);
        StringBuilder xmlBuilder = new StringBuilder(versionXml);
        xmlBuilder.append("<Result>");
        appendXmlBodyChild(xmlBuilder, reqBody);
        xmlBuilder.append("</Result>");
        return xmlBuilder.toString();
    }

    /**
     * 解析报文体
     * 构造报文体 支持报文集合, 支持无限层
     */
    private <T> T analysisXmlBody(String xmlBody, Class<T> objClass) {
        try {
            if (StringUtil.isEmpty(xmlBody)) return null;
            T instance = objClass.newInstance();
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(new InputSource(new StringReader(xmlBody)));
            Element element = document.getDocumentElement();
            analysisXmlBodyChild(instance, element);
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    //构造报文体 子层级递归
    private void appendXmlBodyChild(StringBuilder xmlBuilder, Object object) {
        if (object == null) return;
        List<Field> fields = FieldUtil.getFieldsByAnnotation(object.getClass(), XmlField.class);
        for (Field field : fields) {
            XmlField xmlField = field.getAnnotation(XmlField.class);
            if (field.getType().equals(List.class)) {
                List list = (List) FieldUtil.objGet(object, field);
                if (list == null) list = new ArrayList();
                for (Object item : list) {
                    xmlBuilder.append(String.format("<%s>", xmlField.value()));
                    appendXmlBodyChild(xmlBuilder, item);
                    xmlBuilder.append(String.format("</%s>", xmlField.value()));
                }
            } else if (field.getType().getSuperclass().equals(BaseResult.class)) {
                xmlBuilder.append(String.format("<%s>", xmlField.value()));
                appendXmlBodyChild(xmlBuilder, FieldUtil.objGet(object, field));
                xmlBuilder.append(String.format("</%s>", xmlField.value()));
            } else {
                Object objGet = FieldUtil.objGet(object, field);
                if (objGet == null) continue;
                xmlBuilder.append(String.format("<%s>", xmlField.value()));
                xmlBuilder.append(objGet);
                xmlBuilder.append(String.format("</%s>", xmlField.value()));
            }
        }
    }

    //解析报文体 子节点 递归
    private void analysisXmlBodyChild(Object instance, Node note) {
        try {
            List<Node> overList = new ArrayList<>();
            NodeList childNodes = note.getChildNodes();
            for (int i = 0; i < childNodes.getLength(); i++) {
                Node item = childNodes.item(i);
                if (overList.contains(item)) continue;//在循环内解析的节点，下次不能再重复解析
                Field field = FieldUtil.queryFiledByAnnValue(instance.getClass(), XmlField.class, ann -> ann.value().equals(item.getNodeName()));
                if (field == null) continue;//找查失败，没有在所有字段中找到节点名称对应的字段
                if (field.getType().equals(List.class)) {
                    List<Node> sameList = findNoteListByNodeName(childNodes, item.getNodeName());
                    overList.addAll(sameList);//在循环内解析的节点，下次不能再重复解析
                    ParameterizedType genericType = (ParameterizedType) field.getGenericType();
                    Class aClass = (Class) genericType.getActualTypeArguments()[0];
                    List<Object> objectList = new ArrayList<>();
                    for (Node node : sameList) {
                        Object childObject = aClass.newInstance();
                        analysisXmlBodyChild(childObject, node);
                        objectList.add(childObject);
                    }
                    FieldUtil.objSet(instance, field, objectList);
                } else if (field.getType().getSuperclass().equals(BaseResult.class)) {
                    Object childObject = field.getType().newInstance();
                    analysisXmlBodyChild(childObject, item);
                    FieldUtil.objSet(instance, field, childObject);
                } else if (field.getType().equals(Integer.class)) {
                    FieldUtil.objSet(instance, field, Integer.parseInt(item.getTextContent()));
                } else if (field.getType().equals(Double.class)) {
                    FieldUtil.objSet(instance, field, Double.parseDouble(item.getTextContent()));
                } else {
                    FieldUtil.objSet(instance, field, item.getTextContent());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //找查：通过标签查找节点
    private List<Node> findNoteListByNodeName(NodeList childNodes, String nodeName) {
        ArrayList<Node> sameList = new ArrayList<>();
        for (int j = 0; j < childNodes.getLength(); j++) {
            Node item2 = childNodes.item(j);
            if (item2.getNodeName().equals(nodeName)) {
                sameList.add(item2);
            }
        }
        return sameList;
    }

    public String getReqUrl() {
        return reqUrl;
    }

    public void setReqUrl(String reqUrl) {
        this.reqUrl = reqUrl;
    }

    public String getXmlCategory() {
        return xmlCategory;
    }

    public void setXmlCategory(String xmlCategory) {
        this.xmlCategory = xmlCategory;
    }

    public String getMerchantCode() {
        return merchantCode;
    }

    public void setMerchantCode(String merchantCode) {
        this.merchantCode = merchantCode;
    }

    public String getEncoded() {
        return encoded;
    }

    public void setEncoded(String encoded) {
        this.encoded = encoded;
    }

}